home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Especial Multimedia
/
Especial Multimedia.iso
/
Multimed
/
Prg
/
DLLGIF.ZIP
/
GIFENCOD.C
< prev
next >
Wrap
C/C++ Source or Header
|
1993-08-02
|
15KB
|
551 lines
/***************************************************************************
*
* GIFENCOD.C - GIF Image compression routines
*
* Lempel-Ziv compression based on 'compress'. GIF modifications by
* David Rowley (mgardi@watdcsu.waterloo.edu)
* GIF Image compression - modified 'compress'
*
* Based on: compress.c - File compression ala IEEE Computer, June 1984.
*
* By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
* Jim McKie (decvax!mcvax!jim)
* Steve Davies (decvax!vax135!petsd!peora!srd)
* Ken Turkowski (decvax!decwrl!turtlevax!ken)
* James A. Woods (decvax!ihnp4!ames!jaw)
* Joe Orost (decvax!vax135!petsd!joe)
*
*
* Windows mud-ifications by John Walsh CompuServe ID 70612,1740
* 08 Aug '93
***************************************************************************/
#include <windows.h>
#include <process.h>
#include <stdlib.h>
#include <stdio.h>
#include "gifdll.h"
static void __near char_init(void);
static void __near cl_hash(register long hsize); /* reset code table */
static void __near output(int code );
static void __near cl_block (void); /* table clear for block compress */
static void __near writeerr(void);
static void __near char_out(int c );
static void __near flush_char(void);
static void __near Putword(int w);
static void __near compress(int init_bits);
static int __near GIFNextPixel(void);
static void ClearAll(void);
BOOL FAR PASCAL _export GIFEncode(const char* FName,LPSTR lpDIB);
static const BYTE _huge* lpBits;
static FILE* file;
static int g_init_bits;
static int Width, Height;
static long CountDown;
static int Pass;
static int Interlace;
#define BITS 12
#define HSIZE 5003 /* 80% occupancy */
static int n_bits=0; /* number of bits/code */
static int maxbits = BITS; /* user settable max # bits/code */
static int maxcode=0; /* maximum code, given n_bits */
static int maxmaxcode = (int)1 << 12; /* should NEVER generate this code */
#define MAXCODE(n_bits) (((int) 1 << (n_bits)) - 1)
static long htab [5003];
static unsigned short codetab [HSIZE];
#define HashTabOf(i) htab[i]
#define CodeTabOf(i) codetab[i]
static int hsize = HSIZE; /* for dynamic table sizing */
static long fsize=0;
#define tab_prefixof(i) CodeTabOf(i)
#define tab_suffixof(i) ((char *)(htab))[i]
#define de_stack ((char *)&tab_suffixof((int)1<<BITS))
static int free_ent = 0; /* first unused entry */
static int exit_stat = 0;
static int clear_flg = 0;
static int offset=0;
static long int in_count = 1; /* length of input */
static long int out_count = 0; /* # of codes output (for debugging) */
static int ClearCode=0;
static int EOFCode=0;
static unsigned long cur_accum = 0;
static int cur_bits = 0;
static int a_count=0;
static char accum[ 256 ];
static DWORD line=0;
static unsigned long masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
0x001F, 0x003F, 0x007F, 0x00FF,
0x01FF, 0x03FF, 0x07FF, 0x0FFF,
0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
// Width & Height are global
int __near GIFNextPixel(void)
{
BYTE r;
if( CountDown == 0 )
return EOF;
if(line==0)
{
line=Width;
lpBits=((const BYTE _huge*)lpBits)-(DWORD)Width;
}
CountDown--;
r = lpBits[Width-line];
line--;
return (int)r;
}
/***************************************************************************
FUNCTION :BOOL FAR PASCAL _export GIFEncode(const char* FName,
LPSTR lpDIB)
Here is the function that does all the work for the user. give it a
path/filename with the GIF extension, and a valid pointer to
a packed memory DIB (BITMAPINFO w/RGBQUAD + BITS), and it will do the
rest.
See the readme.txt for limitations
***************************************************************************/
BOOL FAR PASCAL _export GIFEncode(const char* FName,LPSTR lpDIB)
{
int B;
int GWidth,RWidth, RHeight,GHeight;
int LeftOfs, TopOfs, BitsPerPixel;
int Resolution;
int ColorMapSize;
int InitCodeSize;
int i;
LPBITMAPINFO lpBMI;
ClearAll();
lpBMI=(LPBITMAPINFO)lpDIB;
GWidth=(int)lpBMI->bmiHeader.biWidth;
GHeight=(int)lpBMI->bmiHeader.biHeight;
BitsPerPixel=lpBMI->bmiHeader.biBitCount;
Interlace =0;
ColorMapSize = 1 << BitsPerPixel;
line=RWidth = Width = GWidth;
RHeight = Height = GHeight;
LeftOfs = TopOfs = 0;
Resolution = BitsPerPixel;
// Calculate number of bits we are expecting
CountDown = (long)Width * (long)Height;
lpBits= (const BYTE _huge*)(((const BYTE _huge*)lpDIB) +
(((DWORD)CountDown-(DWORD)line)+(DWORD)(ColorMapSize*sizeof(RGBQUAD)
+(DWORD)sizeof(BITMAPINFOHEADER))));
// Indicate which pass we are on (if interlace) NOT SUPPPORTED YET
Pass = 0;
// The initial code size
if( BitsPerPixel <= 1 )
InitCodeSize = 2;
else
InitCodeSize = BitsPerPixel;
// Open the GIF file for binary write
file = fopen( FName, "wb" );
if( file == (FILE *)0 )
{
MessageBox(NULL, "error: could not open output file",
"File Error", MB_ICONSTOP|MB_SYSTEMMODAL );
return FALSE;
}
// Write the Magic header
fputc( 'G', file );
fputc( 'I', file );
fputc( 'F', file );
fputc( '8', file );
fputc( '9', file );
fputc( 'a', file );
// Write out the screen width and height
Putword( RWidth);
Putword( RHeight);
// Indicate that there is a global colour map
B = 0x80; /* Yes, there is a color map */
// OR in the resolution
B |= (Resolution - 1) << 4;//5?
// OR in the Bits per Pixel
B |= (BitsPerPixel - 1);
// Write it out packed fields
fputc( B, file );
// Write out the Background colour
fputc( 0, file );
// Byte of 0's pixel aspect ratio
fputc( 0, file );
// Write out the Global Colour Map
for( i=0; i<ColorMapSize; i++ )
{
fputc( lpBMI->bmiColors[i].rgbRed, file );
fputc( lpBMI->bmiColors[i].rgbGreen, file );
fputc( lpBMI->bmiColors[i].rgbBlue, file );
}
// Write an Image separator
fputc( ',', file );
// Write the Image header
Putword( LeftOfs);
Putword( TopOfs);
Putword( Width);
Putword( Height);
// Write out whether or not the image is interlaced IT'S NOT
if( Interlace )
fputc( 0x40, file );
else
fputc( 0x00, file );
// Write out the initial code size
fputc( InitCodeSize, file );
// Go and actually compress the data
compress( InitCodeSize+1);
// Write out a Zero-length packet (to end the series)
fputc( 0, file );
// Write the GIF file terminator
fputc( ';', file );
// And close the file
fclose( file );
return TRUE;
}
// Write out a word to the GIF file
static void __near Putword(int w)
{
fputc( (w & 0xff), file );
fputc( ((w / 256) & 0xff), file );
}
/*************************************************************************
* Algorithm: use open addressing double hashing (no chaining) on the
* prefix code / next character combination. We do a variant of Knuth's
* algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
* secondary probe. Here, the modular division first probe is gives way
* to a faster exclusive-or manipulation. Also do block compression with
* an adaptive reset, whereby the code table is cleared when the compression
* ratio decreases, but after the table fills. The variable-length output
* codes are re-sized at this point, and a special CLEAR code is generated
* for the decompressor. Late addition: construct the table according to
* file size for noticeable speed improvement on small files. Please direct
* questions about this implementation to ames!jaw.
*************************************************************************/
static void __near compress(int init_bits)
{
register long fcode=0;
register int i = 0;
register int c=0;
register int ent=0;
register int disp=0;
register int hsize_reg=0;
register int hshift=0;
// Set up the necessary values
g_init_bits=init_bits;
offset = 0;
out_count = 0;
clear_flg = 0;
in_count = 1;
maxcode = MAXCODE(n_bits = g_init_bits);
ClearCode = (1 << (init_bits - 1));
EOFCode = ClearCode + 1;
free_ent = ClearCode + 2;
char_init();
ent = GIFNextPixel();
hshift = 0;
for ( fcode = (long) hsize; fcode < 65536L; fcode *= 2L )
hshift++;
hshift = 8 - hshift; /* set hash code range bound */
hsize_reg = hsize;
cl_hash( (long) hsize_reg); /* clear hash table */
output( (int)ClearCode );
while ( (c = GIFNextPixel()) != EOF )
{
in_count++;
fcode = (long) (((long) c << maxbits) + ent);
i = (((int)c << hshift) ^ ent); /* xor hashing */
if ( HashTabOf (i) == fcode )
{
ent = CodeTabOf (i);
continue;
}
else if ( (long)HashTabOf (i) < 0 ) /* empty slot */
goto nomatch;
disp = hsize_reg - i; /* secondary hash (after G. Knott) */
if ( i == 0 )
disp = 1;
probe:
if ( (i -= disp) < 0 )
i += hsize_reg;
if ( HashTabOf (i) == fcode ) {
ent = CodeTabOf (i);
continue;
}
if ( (long)HashTabOf (i) > 0 )
goto probe;
nomatch:
output ( (int) ent );
out_count++;
ent = c;
if ( (long)free_ent < (long)maxmaxcode )
{
CodeTabOf (i) = free_ent++; /* code -> hashtable */
HashTabOf (i) = fcode;
}
else
cl_block();
}
// Put out the final code.
output( (int)ent );
out_count++;
output( (int) EOFCode );
return;
}
/**************************************************************************
* TAG( output )
*
* Output the given code.
* Inputs:
* code: A n_bits-bit integer. If == -1, then EOF. This assumes
* that n_bits =< (long)wordsize - 1.
* Outputs:
* Outputs code to the file.
* Assumptions:
* Chars are 8 bits long.
* Algorithm:
* Maintain a BITS character long buffer (so that 8 codes will
* fit in it exactly). Use the VAX insv instruction to insert each
* code in turn. When the buffer fills up empty it and start over.
**************************************************************************/
static void __near output(int code )
{
cur_accum &= masks[ cur_bits ];
if( cur_bits > 0 )
cur_accum |= ((long)code << cur_bits);
else
cur_accum = code;
cur_bits += n_bits;
while( cur_bits >= 8 )
{
char_out( (unsigned int)(cur_accum & 0xff) );
cur_accum >>= 8;
cur_bits -= 8;
}
/*
* If the next entry is going to be too big for the code size,
* then increase it, if possible.
*/
if ( free_ent > maxcode || clear_flg )
{
if( clear_flg )
{
maxcode = MAXCODE (n_bits = g_init_bits);
clear_flg = 0;
}
else
{
n_bits++;
if ( n_bits == maxbits )
maxcode = maxmaxcode;
else
maxcode = MAXCODE(n_bits);
}
}
if( code == EOFCode )
{
/*
* At EOF, write the rest of the buffer.
*/
while( cur_bits > 0 )
{
char_out( (unsigned int)(cur_accum & 0xff) );
cur_accum >>= 8;
cur_bits -= 8;
}
flush_char();
fflush( file );
if( ferror( file ) )
writeerr();
}
}
/*
* Clear out the hash table
*/
static void __near cl_block (void) /* table clear for block compress */
{
cl_hash ( (long) hsize );
free_ent = ClearCode + 2;
clear_flg = 1;
output( (int)ClearCode );
}
static void __near cl_hash(register long hsize) /* reset code table */
{
register long *htab_p = htab+hsize;
register long i=0;
register long m1 = -1;
i = hsize - 16;
do
{ /* might use Sys V memset(3) here */
*(htab_p-16) = m1;
*(htab_p-15) = m1;
*(htab_p-14) = m1;
*(htab_p-13) = m1;
*(htab_p-12) = m1;
*(htab_p-11) = m1;
*(htab_p-10) = m1;
*(htab_p-9) = m1;
*(htab_p-8) = m1;
*(htab_p-7) = m1;
*(htab_p-6) = m1;
*(htab_p-5) = m1;
*(htab_p-4) = m1;
*(htab_p-3) = m1;
*(htab_p-2) = m1;
*(htab_p-1) = m1;
htab_p -= 16;
} while ((i -= 16) >= 0);
for ( i += 16; i > 0; i-- )
*--htab_p = m1;
}
static void __near writeerr(void)
{
MessageBox(NULL,"Error writing to gif file",
"GIF write error",MB_ICONSTOP|MB_SYSTEMMODAL);
return;
}
/*
* Set up the 'byte output' routine
*/
static void __near char_init(void)
{
a_count = 0;
}
/*
* Add a character to the end of the current packet, and if it is 254
* characters, flush the packet to disk.
*/
static void __near char_out(int c )
{
accum[ a_count++ ] = c;
if( a_count >= 254 )
flush_char();
}
/*
* Flush the packet to disk, and reset the accumulator
*/
static void __near flush_char(void)
{
if( a_count > 0 )
{
fputc( a_count, file );
fwrite( accum, 1, a_count, file );
a_count = 0;
}
}
/* The End */
static void ClearAll(void)
{
// maxbits = BITS; /* user settable max # bits/code */
free_ent = 0; /* first unused entry */
clear_flg = 0;
offset=0;
in_count = 1; /* length of input */
out_count = 0; /* # of codes output (for debugging) */
cur_accum = 0;
cur_bits = 0;
a_count=0;
line=0;
}